package clasificador.redneuronal.neurona;

import java.util.Arrays;

import utils.Logger;

public class NeuronaSoftmax extends Neurona {

    double[] weights;
    public Signal<Double> expSignal;
    private boolean expUpdated;
    private double expValue;
    private GrupoNeuronasSoftmax grupo;
    
    @Override
    public void invalidate() {
        super.invalidate();
        this.expSignal.invalidate();
        this.grupo.invalidate();
    }

    @Override
    public double errorFunction(double[] errors) {
        //Logger.info("Calculando error para SOFTMAX a partir de errores: " + Arrays.toString(errors));
        double error = 0.0;

        for (int i = 0; i < errors.length; i++) {
            error += errors[i];
        }

        //Logger.info("Error resultante: " + error);
        return error;
    }

    @Override
    public double activationFunction(double[] inputs) {
        //Logger.info("Suma de pesos: " + sum);
        return this.expSignal.getValue() / grupo.sumSignal.getValue();
    }

    @Override
    public double derivative(int i, double[] inputs, double output) {        
        return output * (1-output) * weights[i + 1];
    }

    @Override
    public void train(double learningCoefficient) {
        //Logger.info("***NEURONA SOFTMAX***");
        //Logger.info("Pesos antes de la actualizacion: " + Arrays.toString(weights));		        
        double output = this.outputSignal.getValue();
        //Logger.info("Output: " + output);
        double error = this.errorSignal.getValue();
        //Logger.info("Error: " + error);
        this.weights[0] -= learningCoefficient * output * (1-output) * error;
        for (int i = 0; i < this.weights.length - 1; i++) {
            this.weights[i + 1] -= learningCoefficient * output * (1-output) * error * this.getInputValue(i);
        }
        //Logger.info("Pesos tras de la actualizacion: " + Arrays.toString(weights));
        //Logger.info("***FIN NEURONA SOFTMAX***");
    }

    public void setGrupo(GrupoNeuronasSoftmax grupo) {
        this.grupo = grupo;
    }

    @Override
    public void init() {
        this.weights = new double[this.getNInputs() + 1];

        weights[0] = 0.0;

        for (int i = 1; i <= this.getNInputs(); i++) {
            weights[i] = 0.001 * (2 * Math.random() - 1);
        }

        expUpdated = false;

        this.expSignal = new Signal<Double>() {
            @Override
            public void recalculate() {
                this.value = recalculateExpValue();
            }
        };

        if (this.grupo == null) {
            throw new RuntimeException("Esta neurona softmax no pertenece a ningun grupo.");
        }
        this.grupo.addNeurona(this);
    }

    protected double recalculateExpValue() {
        double sum = weights[0];
        for (int i = 0; i < this.getNInputs(); i++) {
            sum += weights[i + 1] * this.getInputValue(i);
        }

        return Math.exp(sum);
    }
}
